import { ChangeDetectionStrategy, Component, Input, TemplateRef, ViewChild } from '@angular/core';
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ButtonModule } from '../button/api';
import { DropdownModule } from './dropdown.module';
import { MenuItem } from './interface';

const menus = [
  {
    id: '1',
    label: 'MenuItem 01',
    items: [
      {
        id: '11',
        label: 'MenuItem 11',
      },
      {
        id: '12',
        label: 'MenuItem 12',
      },
    ],
  },
  {
    id: '2',
    label: 'MenuItem 02',
    divide: true,
    items: [
      {
        id: '21',
        label: 'MenuItem 21',
        items: [
          {
            id: '211',
            label: 'MenuItem 211',
          },
          {
            id: '212',
            label: 'MenuItem 212',
            divide: true,
          },
          {
            id: '213',
            label: 'MenuItem 213',
          },
        ],
      },
      {
        id: '22',
        label: 'MenuItem 22',
      },
    ],
  },
  {
    id: '3',
    label: 'MenuItem 03',
  },
  {
    id: '4',
    label: 'MenuItem 04',
  },
];

@Component({
  template: `
    <button
      lv-button
      lv-dropdown
      [lvDropdownSize]="lvDropdownSize"
      [lvDropdownMenus]="lvDropdownMenus"
      [lvDropdownTrigger]="lvDropdownTrigger"
      [lvDropdownDisabled]="lvDropdownDisabled"
      [lvDropdownPanelClass]="lvDropdownPanelClass"
      [lvDropdownItemClickClose]="lvDropdownItemClickClose"
      (lvDropdownShow)="onOpen()"
      (lvDropdownHide)="onClose()"
      (lvDropdownItemClick)="onClick()"
    >
      Default
    </button>
    <button lv-button lv-dropdown [lvDropdownMenus]="menus">Customize</button>
    <button lv-button lv-dropdown [lvDropdownMenus]="menus" [lvDropdownLabelTemplate]="label">Customize</button>
    <ng-template #item2Tpl>
      <span style="color: #b807ff;" class="customItem">MenuItem 02</span>
    </ng-template>

    <ng-template #label let-data>
      <span style="color: red;" class="customLabel">{{ data.label }}</span>
    </ng-template>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestDropDownComponent {
  @Input() lvDropdownMenus: MenuItem[];
  @Input() lvDropdownTrigger: 'click' | 'hover' | 'contextmenu' = 'click';
  @Input() lvDropdownSize: 'large' | 'default' = 'default';
  @Input() lvDropdownItemClickClose: boolean = true;
  @Input() lvDropdownPanelClass: string = '';
  @Input() lvDropdownDisabled: boolean = false;

  menus: MenuItem[];

  @ViewChild('item2Tpl', { static: true }) item2Tpl: TemplateRef<any>;

  initMenus() {
    this.menus = [
      {
        id: '1',
        label: 'MenuItem 01',
        hidden: true,
      },
      {
        id: '2',
        label: this.item2Tpl,
      },
      {
        id: '3',
        label: 'MenuItem 03',
        onClick: () => this.itemClick(),
      },
      {
        id: '4',
        label: 'MenuItem 04',
        disabled: true,
        onClick: () => this.itemClick(),
      },
    ];
  }

  itemClick() {}

  onClick() {}

  onOpen() {}

  onClose() {}
}

describe('DropDownComponent', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [TestDropDownComponent],
      imports: [ButtonModule, DropdownModule, NoopAnimationsModule],
      teardown: { destroyAfterEach: true },
    }).compileComponents();
  }));

  describe('render correctly', () => {
    let component: TestDropDownComponent;
    let fixture: ComponentFixture<TestDropDownComponent>;
    const renderComponent = param => {
      for (const [key, value] of Object.entries(param)) {
        component[key] = value;
      }
      fixture.detectChanges();
    };
    const getButtonGroup = () => fixture.nativeElement.querySelectorAll('.lv-button');
    const getPopover = () => document.querySelector('.lv-dropdown-panel');

    beforeEach(() => {
      fixture = TestBed.createComponent(TestDropDownComponent);
      component = fixture.componentInstance;
      component.lvDropdownMenus = menus;
    });

    it('with default', fakeAsync(() => {
      fixture.detectChanges();
      const button = getButtonGroup()[0];
      button.dispatchEvent(new Event('click'));
      tick(500);
      expect(getPopover()).toMatchSnapshot();
    }));

    it('with customize', fakeAsync(() => {
      component.initMenus();
      tick(500);
      fixture.detectChanges();
      const button = getButtonGroup()[1];
      button.dispatchEvent(new Event('click'));
      tick(1000);
      const popover = getPopover();
      expect(popover).toMatchSnapshot();
      const menuItem = popover.querySelector('.customItem');
      expect(menuItem).toBeTruthy();
    }));

    it('with lvDropdownLabelTemplate', fakeAsync(() => {
      component.initMenus();
      tick(500);
      fixture.detectChanges();
      const button = getButtonGroup()[2];
      button.dispatchEvent(new Event('click'));
      tick(1000);
      const popover = getPopover();
      expect(popover).toMatchSnapshot();
      const menuItem = popover.querySelector('.customItem');
      const menuItems = popover.querySelectorAll('.customLabel');
      expect(menuItem).toBeTruthy();
      expect(menuItems[2].textContent).toEqual('MenuItem 04');
    }));

    it('with lvDropdownSize', fakeAsync(() => {
      renderComponent({ lvDropdownSize: 'large' });
      fixture.detectChanges();
      getButtonGroup()[0].dispatchEvent(new Event('click'));
      tick(1000);
      fixture.detectChanges();
      expect(getPopover()).toMatchSnapshot();
      expect(document.querySelector('.lv-dropdown-size-large')).toEqual(getPopover());
    }));
  });

  describe('works correctly', () => {
    let component: TestDropDownComponent;
    let fixture: ComponentFixture<TestDropDownComponent>;
    const renderComponent = param => {
      for (const [key, value] of Object.entries(param)) {
        component[key] = value;
      }
      fixture.detectChanges();
    };
    const getButtonGroup = () => fixture.nativeElement.querySelectorAll('.lv-button');
    const getPopover = () => document.querySelector('.lv-dropdown-panel');

    beforeEach(() => {
      fixture = TestBed.createComponent(TestDropDownComponent);
      component = fixture.componentInstance;
      component.lvDropdownMenus = menus;
    });

    it('with lvDropdownDisabled', fakeAsync(() => {
      renderComponent({ lvDropdownDisabled: true });
      getButtonGroup()[0].dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
      expect(getPopover()).toBeNull();
    }));

    it('with lvDropdownTrigger is hover', fakeAsync(() => {
      renderComponent({ lvDropdownTrigger: 'hover' });
      getButtonGroup()[0].dispatchEvent(new Event('mouseenter'));
      tick(500);
      fixture.detectChanges();
      expect(getPopover()).toBeTruthy();
      getButtonGroup()[0].dispatchEvent(new Event('mouseleave'));
      tick(1000);
      fixture.detectChanges();
      tick(1500);
      expect(getPopover()).toBeNull();
    }));

    it('with lvDropdownTrigger is contextmenu', fakeAsync(() => {
      renderComponent({ lvDropdownTrigger: 'contextmenu' });
      getButtonGroup()[0].dispatchEvent(new Event('contextmenu'));
      tick(500);
      fixture.detectChanges();
      expect(getPopover()).toBeTruthy();
    }));

    it('with lvDropdownPanelClass', fakeAsync(() => {
      renderComponent({ lvDropdownPanelClass: 'auiDropdown' });
      getButtonGroup()[0].dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
      expect(document.querySelector('.auiDropdown')).toEqual(getPopover());
    }));

    it('without lvDropdownItemClickClose', fakeAsync(() => {
      renderComponent({ lvDropdownItemClickClose: false });
      getButtonGroup()[0].dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
      const popover = getPopover();
      const item = popover.querySelectorAll('.lv-dropdown-item');
      item[0].dispatchEvent(new Event('click'));
      tick(1000);
      fixture.detectChanges();
      tick(1500);
      expect(getPopover()).toBeTruthy();
    }));

    it('with lvDropdownShow | lvDropdownHide', fakeAsync(() => {
      jest.spyOn(component, 'onOpen');
      jest.spyOn(component, 'onClose');
      fixture.detectChanges();
      getButtonGroup()[0].dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
      expect(component.onOpen).toHaveBeenCalled();
      getButtonGroup()[0].dispatchEvent(new Event('click'));
      tick(1000);
      fixture.detectChanges();
      tick(1500);
      expect(component.onClose).toHaveBeenCalled();
    }));

    it('with lvDropdownItemClick', fakeAsync(() => {
      jest.spyOn(component, 'onClick');
      jest.spyOn(component, 'itemClick');
      fixture.detectChanges();
      getButtonGroup()[0].dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
      const menuItems = document.querySelectorAll('.lv-dropdown-item');
      menuItems[0].dispatchEvent(new Event('click'));
      tick(1000);
      fixture.detectChanges();
      expect(component.onClick).toHaveBeenCalled();
    }));
  });
});
